export type NumberObj = { [key: string]: number };
export interface AnimateOption {
    duration: number,
    from?: NumberObj,
    to: NumberObj,
    ease: string,
    onUpdate?: (val: NumberObj) => void,
    onStart?: () => void,
    onComplete?: (val: NumberObj) => void,
}
var timer = 0;

export default class AnimationPlugin {

    options: AnimateOption;
    startTime: number = Date.now();
    progress = 0;   // 动画执行进度 0-1之间
    usedTime = 0;   // 动画已经运行的时间
    easeFunc: (t: number) => number;
    process: NumberObj = {}   // 当前帧缓存的值

    constructor(options: AnimateOption) {
        this.options = options;
        if (!this.options.from) this.options.from = {}
        this.easeFunc = this.getEaseFunction(this.options.ease);
    }

    // 获取缓动函数  
    getEaseFunction(easeName: string) {  // t => 逐渐变大
        switch (easeName) {
            case 'linear':
                return (t: number) => t;
            case 'quadIn':
                return (t: number) => t * t;  // 先慢后快  0.3*0.3 = 0.09  0.8*0.8 = 0.64
            case 'quadOut':
                return (t: number) => -(t * (t - 2)); // 先快后慢
            case 'quadInOut':
                return (t: number) => {
                    t /= 0.5;
                    if (t < 1) return 0.5 * t * t;
                    t--;
                    return -0.5 * (t * (t - 2) - 1);
                };
            case 'bounceOut':
                return (t: number) => {
                    const n1 = 7.5625;
                    const d1 = 2.75;
                    if (t < (1 / d1)) {
                        return n1 * t * t;
                    } else if (t < (2 / d1)) {
                        return n1 * (t -= (1.5 / d1)) * t + 0.75;
                    } else if (t < (2.5 / d1)) {
                        return n1 * (t -= (2.25 / d1)) * t + 0.9375;
                    } else {
                        return n1 * (t -= (2.625 / d1)) * t + 0.984375;
                    }
                };
            case (() => easeName.startsWith('cubicBezier') ? easeName : '')():
                function getPntIn3Bezier(t: number, y1: number, y2: number) {
                    // 确保t在0到1之间  
                    t = Math.max(0, Math.min(1, t));
                    // 三次贝塞尔曲线的公式  
                    var oneMinusT = 1 - t;
                    var oneMinusTSquared = Math.pow(oneMinusT, 2);
                    var oneMinusTCubed = Math.pow(oneMinusT, 3);
                    var tSquared = Math.pow(t, 2);
                    var tCubed = Math.pow(t, 3);
                    // 只需要Y坐标 
                    // var x = 3 * oneMinusTSquared * t * cp1.x + 3 * oneMinusT * tSquared * cp2.x + tCubed;
                    var y = 3 * oneMinusTSquared * t * y1 + 3 * oneMinusT * tSquared * y2 + tCubed;
                    // 返回曲线上的点  
                    return y;
                }
                const params = easeName.match(/cubicBezier\(([^)]+)\)/);
                if (params && params[1]) {
                    const arr = params[1].split(",").map(str => Number(str))
                    return (t: number) => getPntIn3Bezier(t, arr[1], arr[3])  // 他传过来的x坐标并不重要,但是习惯了传坐标就传吧
                }
                return (t: number) => getPntIn3Bezier(t, -0.57, 1.46)
            // 可以继续添加更多缓动函数...
            default:
                return (t: number) => t; // 默认线性  
        }
    }

    // 动画循环  
    go() {
        if (!this.startTime) {
            if (this.options.onStart) this.options.onStart()
            this.startTime = Date.now();
        }
        const elapsed = Date.now() - this.startTime;  // 与当前时间之差
        const t = elapsed / this.options.duration;  // 算出累计耗时占规定总时长的百分之多少,t的起点值是0, t的终点值是1

        if (t > 1) {
            if (this.options.onComplete) this.options.onComplete(1)
            return; // 停止动画
        }
        this.update(t);
        // 使用requestAnimationFrame请求下一帧  
        timer = requestAnimationFrame(this.go.bind(this));
    }

    stop() {
        if (timer) cancelAnimationFrame(timer);
        this.startTime = 0;
        this.progress = 0;
        this.usedTime = 0;
    }

    pause() {
        if (timer) cancelAnimationFrame(timer);
        this.usedTime = Date.now() - this.startTime;
        this.startTime = 0;
    }

    resume() {
        Object.keys(this.process).forEach(key => {
            if (this.options.from) this.options.from[key] = this.process[key];
        })
        this.options.duration -= this.usedTime;
        this.go();
    }

    reverse() {
        if (!this.startTime) {
            this.startTime = Date.now();
        }
        const elapsed = Date.now() - this.startTime;  // 与当前时间之差
        const t = elapsed / this.usedTime;  // 算出累计耗时占规定总时长的百分之多少,t的起点值是0, t的终点值是1
        const usedT = this.usedTime / this.options.duration;  // 算出累计耗时占规定总时长的百分之多少,t的起点值是0, t的终点值是1

        if (usedT - t < 0) {
            return; // 停止动画
        }
        this.update(usedT - t)
        timer = requestAnimationFrame(this.reverse.bind(this));
    }

    update(t: number) {
        this.progress = this.easeFunc(t);
        Object.keys(this.options.to).forEach(key => {
            const startValue = (this.options.from && this.options.from[key]) ? this.options.from[key] : 0;
            const endValue = this.options.to[key];
            const value = startValue + this.progress * (endValue - startValue)
            this.process[key] = value;
            if (this.options.onUpdate) this.options?.onUpdate(this.process);
        });
    }

}  